Unreal custom shading model

Created by miccall (转载请注明出处 miccall.tech)

基于 Unreal engine 4.20.2

1. EngineTypes.h

    //452 line    
    UENUM()
        enum EMaterialShadingModel
        {
                MSM_Unlit                UMETA(DisplayName="Unlit"),
                MSM_DefaultLit            UMETA(DisplayName="Default Lit"),
                MSM_Subsurface            UMETA(DisplayName="Subsurface"),
                MSM_PreintegratedSkin          UMETA(DisplayName="Preintegrated Skin"),
                MSM_ClearCoat            UMETA(DisplayName="Clear Coat"),
                MSM_SubsurfaceProfile          UMETA(DisplayName="Subsurface Profile"),
                MSM_TwoSidedFoliage        UMETA(DisplayName="Two Sided Foliage"),
                MSM_Hair                UMETA(DisplayName="Hair"),
                MSM_Cloth                UMETA(DisplayName="Cloth"),
                MSM_Eye                UMETA(DisplayName="Eye"),
                MSM_MAX,
        };

此枚举确定在“材质编辑器”内的“shading model ”下拉列表中显示的内容

添加自己的 shading model :

        MSM_Eye                UMETA(DisplayName="Eye"),
        MSM_StylizedShadow        UMETA(DisplayName="Stylized Shadow"),
        MSM_MAX,

2. Material.cpp


    // 5621     
    bool UMaterial::IsPropertyActive(EMaterialProperty InProperty) const 

UE 对 material 上的每个可能的 shading model 引脚 调用此函数 。
我们添加一个 Custonm 引脚 :

        switch (InProperty)
        {
                // 。。。
                case MP_CustomData0 :
                        Active = ShadingModel == MSM_ClearCoat || ShadingModel == MSM_Hair || ShadingModel == MSM_Cloth || ShadingModel == MSM_Eye || ShadingModel == MSM_StylizedShadow ;
                // 。。。 
        }

此代码仅更改材质编辑器中的UI 。 Custom Data 0 和 Custom Data 1 都是单通道 float 属性 。

一旦我们修改了材质编辑器以便能够选择我们的新着色模型,我们需要确保我们的着色器知道它们何时被设置为使用我们的着色模型。

3. MaterialShared.cpp

    // 1255    
    void FMaterial::SetupMaterialEnvironment(
                EShaderPlatform Platform,
                const FUniformExpressionSet& InUniformExpressionSet,
                FShaderCompilerEnvironment& OutEnvironment
        ) const

此方法 允许你查看各种配置因素(例如 material 上的属性)。 OutEnvironment通过添加其他定义来修改变量

GetShadingModel() 并添加我们的 MSM_StylizedShadow 案例

    //1445    

    switch(GetShadingModel())
        {
                ...

                case MSM_Eye:            OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_EYE"),                                    TEXT("1")); break;

                case MSM_StylizedShadow:    OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_STYLIZEDSHADOW"),                              TEXT("1")); break;

                default:

                ...

        };

当材质的Shading Model设置为MSM_StylizedShadowHLSL编译器时,将设置MATERIAL_SHADINGMODEL_STYLIZED_SHADOW为预处理器定义.

4. 更新 GBuffer着色模型 ID

通过MATERIAL_SHADINGMODEL_STYLIZED_SHADOW我们可以开始对着色器进行更改。我们需要做的第一件事是将新的着色模型ID写入GBuffer这允许DeferredLightPixelShader知道在运行光照计算时要尝试使用的着色模型。

DeferredShadingCommon.ush


    //269    

        #define SHADINGMODELID_UNLIT                0
        #define SHADINGMODELID_DEFAULT_LIT            1

         . . .

        #define SHADINGMODELID_EYE                    9
        #define SHADINGMODELID_STYLIZED_SHADOW        10 
        #define SHADINGMODELID_NUM                    11
        #define SHADINGMODELID_MASK                    0xF    

我们需要告诉着色器将此着色模型ID写入Gbuffer . 然后 ,


    // 731    
        float3 GetShadingModelColor(uint ShadingModelID) 
        {
                #if PS4_PROFILEelse if (ShadingModelID == SHADINGMODELID_EYE) return float3(0.3f, 1.0f, 1.0f);
                        else if (ShadingModelID == SHADINGMODELID_STYLIZED_SHADOW ) return float3(0.4f, 0.0f, 0.8f); // Purple
                        else return float3(1.0f, 1.0f, 1.0f); // White
                #else
                        switch(ShadingModelID)
                        {case SHADINGMODELID_EYE: return float3(0.3f, 1.0f, 1.0f);
                         case SHADINGMODELID_STYLIZED_SHADOW: return float3(0.4f, 0.0f, 0.8f); // Purple
                        default: return float3(1.0f, 1.0f, 1.0f); // White
                        }
                #endif
        }

现在我们需要告诉BasePassPixelShader将正确的ID写入Shading Model ID纹理.

ShadingModelsMaterial.ush

    //11
        void SetGBufferForShadingModel(
                in out FGBufferData GBuffer, 
                in const FMaterialPixelParameters MaterialParameters,
                const float Opacity,
                const half3 BaseColor,
                const half  Metallic,
                const half  Specular,
                const float Roughness,
                const float3 SubsurfaceColor,
                const float SubsurfaceProfile
        )

此函数允许每个着色模型选择如何将各种PBR数据通道写入FGBufferData结构。
唯一需要做的就是确保分配了GBuffer.ShadingModelID。如果我们希望使用Custom Data 0材质编辑器中的通道,就可以在此处查询该值并将其写入GBuffer。


    //128
        #elif MATERIAL_SHADINGMODEL_EYE
                GBuffer.ShadingModelID = SHADINGMODELID_EYE;
                GBuffer.CustomData.x = EncodeSubsurfaceProfile(SubsurfaceProfile).x;
                GBuffer.CustomData.w = 1.0f - saturate(GetMaterialCustomData0(MaterialParameters));    // Opacity = 1.0 - Iris Mask
        #elif MATERIAL_SHADINGMODEL_STYLIZED_SHADOW
                GBuffer.ShadingModelID = SHADINGMODELID_STYLIZED_SHADOW; 
                GBuffer.CustomData.x = GetMaterialCustomData0(MaterialParameters);
        #if IRIS_NORMAL

BasePassCommon.ush

    //40
        // Only some shader models actually need custom data.
        #define WRITES_CUSTOMDATA_TO_GBUFFER        (USES_GBUFFER && (MATERIAL_SHADINGMODEL_SUBSURFACE || MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || MATERIAL_SHADINGMODEL_CLEAR_COAT || MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || MATERIAL_SHADINGMODEL_HAIR || MATERIAL_SHADINGMODEL_CLOTH || MATERIAL_SHADINGMODEL_EYE || MATERIAL_SHADINGMODEL_STYLIZED_SHADOW ))

5. 改变衰减计算

DeferredLightingCommon.ush

    //256
        float4 GetDynamicLighting(float3 WorldPosition, float3 CameraVector, FGBufferData GBuffer, float AmbientOcclusion, uint ShadingModelID, FDeferredLightData LightData, float4 LightAttenuation, float Dither, uint2 SVPos)

虚幻使用以下计算来确定最终光倍数:LightColor (NoL SurfaceAttenuation)
我们将创建一个新的光衰减变量 。

        float3 AttenuationColor = 0.f;
        BRANCH 
        if(ShadingModelID == SHADINGMODELID_STYLIZED_SHADOW)
        { 
                float Range = GBuffer.CustomData.x * 0.5f; 
                AttenuationColor = LightColor *((DistanceAttenuation * LightRadiusMask * SpotFalloff)* smoothstep(0.5f  -  Range,0.5f + Range,SurfaceShadow)* 0.1f; 
        } 
        else 
        { 
                AttenuationColor = LightColor *(NoL * SurfaceAttenuation); 
        }

新的着色系统 ,改了 阴影的计算 ,我们先不用这一套 。

6. 更改表面着色

ShadingModels.ush


    //910

        FDirectLighting IntegrateBxDF( FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow )

                switch(GBuffer.ShadingModelID)
                {
                    ...
                    case SHADINGMODELID_EYE:
                        return EyeBxDF(GBuffer,N,V,L,Falloff,NoL,AreaLight,Shadow);
                    case SHADINGMODELID_STYLIZED_SHADOW:
                        return StylizedShadowShading(GBuffer,N,V,L,Falloff,NoL,AreaLight,Shadow);
                    default:
                        return (FDirectLighting)0;
                }

然后我们来定义这个 StylizedShadowShading 函数


    FDirectLighting StylizedShadowShading(FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow)
        {
                float    Range = GBuffer.CustomData.x * 0.5f;
                float3     H = normalize(V+L);
                float    NoH = saturate( dot(N, H));

                FDirectLighting Lighting ;
                Lighting.Diffuse = AreaLight.FalloffColor * (Falloff * NoL) * Diffuse_Lambert( GBuffer.DiffuseColor );
                Lighting.Specular = saturate( smoothstep(  0.5f-Range , 0.5f + Range , D_GGX(GBuffer.Roughness,  NoH)) * GBuffer.SpecularColor );
                Lighting.Transmission = 0 ;

                return Lighting ;
        }

7. 支持半透明

BasePassPixelShader.usf


    //1030

        // Volume lighting for lit translucency

        // Volume lighting for lit translucency
        // #if (MATERIAL_SHADINGMODEL_DEFAULT_LIT || MATERIAL_SHADINGMODEL_SUBSURFACE) && (MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE) && !SIMPLE_FORWARD_SHADING && !FORWARD_SHADING
        #if    (MATERIAL_SHADINGMODEL_DEFAULT_LIT || MATERIAL_SHADINGMODEL_SUBSURFACE  || MATERIAL_SHADINGMODEL_STYLIZED_SHADOW ) && (MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE) && !SIMPLE_FORWARD_SHADING && !FORWARD_SHADING
                Color += GetTranslucencyVolumeLighting(MaterialParameters, PixelMaterialInputs, BasePassInterpolants, GBuffer, IndirectIrradiance);
        #endif

8. 自定义接口名称

MaterialGraph.cpp

   // 527

       FText UMaterialGraph::GetCustomDataPinName( uint32 Index ) const

在 index == 0 的那个条件下 ,默认为custom data 0 , 我们可以更改他的显示名称:

        if( Index == 0 )
        {
                switch( Material->GetShadingModel() )
                {

                        ...

                        case MSM_Eye:
                                return LOCTEXT("IrisMask", "Iris Mask");
                        case MSM_StylizedShadow :
                                return LOCTEXT("StylizedShadow", "StylizedShadow");
                        default:
                                return LOCTEXT("CustomData0", "Custom Data 0");
                }
        }